package token

import (
	"errors"
	"time"

	"github.com/gogf/gf/container/gmap"
	"github.com/gogf/gf/crypto/gaes"
	"github.com/gogf/gf/database/gredis"
	"github.com/gogf/gf/encoding/gbase64"
	"github.com/gogf/gf/os/gsession"
	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/text/gstr"
	"github.com/gogf/gf/util/gconv"
	"github.com/gogf/gf/util/guid"
)

const (
	encryptKey         string = "abc789451623pcg789456123qwe78945" // 32位key
	delimiter          string = "|"                                // 设备编号不能包含|
	sessionLastTimeKey string = "__last_time__"                    // session 最后访问时间key 毫秒
)

var (
	SessionStore *gsession.StorageRedisHashTable // SessionStore session存储
	SessionTTL   time.Duration                   // SessionTTL session过期时间
	Redis        *gredis.Redis                   // Redis     redis单例
)

// Info token包含信息
type Info struct {
	Session    *gsession.Session // session对象
	UserName   string            // 用户名
	Token      string            // token
	DeviceNo   string            // 设备编号
	CreateTime int64             // token生成时间 毫秒
	NonceStr   string            // 随机字符串
}

// Generate 生成token
// 第一个参数 username
// 第二个参数 deviceNo
func Generate(param ...string) (info *Info, err error) {
	info = &Info{
		CreateTime: gtime.TimestampMilli(),
		NonceStr:   guid.S(),
	}
	cnt := len(param)
	if cnt > 0 {
		info.UserName = param[0]
		if cnt > 1 {
			info.DeviceNo = param[1]
		}
	}
	if info.DeviceNo == "" {
		info.DeviceNo = guid.S()
	}
	tokenStr := info.NonceStr + delimiter + info.UserName + delimiter + info.DeviceNo + delimiter + gconv.String(info.CreateTime)
	tb, err := gaes.Encrypt([]byte(tokenStr), []byte(encryptKey))
	if err != nil {
		err = errors.New("[token]encrypt error " + err.Error())
		return
	}
	info.Token = gbase64.EncodeToString(tb)
	info.Session = Save(info.Token, &gmap.StrAnyMap{})
	return
}

// Auth 验证Token
func Auth(token string) (info *Info, err error) {
	info = &Info{}
	if IsExpired(token) {
		err = errors.New("[token]invalid")
		return
	}
	token64, err := gbase64.DecodeToString(token)
	if err != nil {
		err = errors.New("[token]decode error" + err.Error())
		return
	}
	decryptToken, err := gaes.Decrypt([]byte(token64), []byte(encryptKey))
	if err != nil {
		err = errors.New("[token]decrypt error" + err.Error())
		return
	}
	tokenArray := gstr.Split(string(decryptToken), delimiter)
	if len(tokenArray) != 4 {
		err = errors.New("[token]len error")
		return
	}
	info.Token = token
	info.NonceStr = tokenArray[0]
	info.UserName = tokenArray[1]
	info.DeviceNo = tokenArray[2]
	info.CreateTime = gconv.Int64(tokenArray[3])
	return
}

// IsExpired 判断token是否过期
func IsExpired(token string) bool {
	res, _ := Redis.DoVar("EXISTS", token)

	return res.Int() == 0
}

// Save 保存sessions 数据
func Save(token string, data *gmap.StrAnyMap) *gsession.Session {
	data.Iterator(func(k string, v interface{}) bool {
		_ = SessionStore.Set(token, k, v, SessionTTL)
		return true
	})
	if data.IsEmpty() {
		_ = SessionStore.Set(token, sessionLastTimeKey, gtime.TimestampMilli(), SessionTTL)
	}
	_ = SessionStore.UpdateTTL(token, SessionTTL)
	sess := gsession.New(SessionTTL, SessionStore).New(token)
	_ = sess.Remove("") // 不删任何东西,使session.dirty=true
	return sess
}
